import type {
  AdjustElementStyleConfig,
  AdjustElementStyleGroupConfig,
} from '../../../lib/abstract'
import type { DataChangeType, DeleteLinePayload } from '../interfaces'
import { UIComponent } from '../../../lib/abstract'
import { CopyButtonUpdateType } from '../../../lib/utils/base/dom'
import { ToolKit } from '../../../lib/utils/toolkit'
import { logger } from '../../../types/constants'
import { SYMBOLS } from '../../../types/interfaces'
import { SelectorUpdateTimeMap, StyleIdManager } from './persist'
import { styles } from './styles'

export interface PreviewConfigProps {
  id?: string
  /** 清空全部回调 */
  onClickClearAll: () => void

  /**
   * 删除行回调
   * @param lineNum 行号
   * @param payload 删除上下文数据（仅样式属性行有效）
   */
  onClickDeleteLine: (
    lineNum: number,
    payload: DeleteLinePayload
  ) => void

  /** 更新完成回调 */
  onUpdated: (sourceType: DataChangeType) => void
}

export class PreviewConfig extends UIComponent<PreviewConfigProps> {
  id: string = 'preview-config'
  private lastUpdateTime = new Date()
  private createTimeStorageKey = `${this.id}-create-time`
  private groupStyles: AdjustElementStyleGroupConfig = {}
  private readonly lineDeleteIcon: string = '-'
  private latestSelector: string | undefined = undefined

  get className(): string {
    return 'preview-config'
  }

  constructor(props: PreviewConfigProps) {
    super(props)
  }

  ELEMENT_SELECTORS = {
    timeDisplay: { selector: '.preview-header .stats', dynamic: false, unique: false },
    clearAll: { selector: '.header-actions .clear-all', dynamic: false, unique: false },
    copyBtn: { selector: '.header-actions .copy-btn', dynamic: false, unique: false },
    syncBtn: { selector: '.header-actions .sync-btn', dynamic: false, unique: false },
    lineDeleteBtn: { selector: '.delete-btn', dynamic: true, unique: false },
    codeContainer: { selector: '.preview-content', dynamic: false, unique: false },
    codeConfig: { selector: '.preview-content pre .line .content', dynamic: false, unique: false },
  }

  private selectorTimeMap = SelectorUpdateTimeMap.getInstance()
  styleIdManager: StyleIdManager = StyleIdManager.getInstance()

  /**
   * 渲染组件
   * @param styles 样式配置
   */
  render(styles: AdjustElementStyleConfig[]): string {
    return `
      <div class="${this.className}">
        <div class="preview-header">
          <span class="stats">
            ${this.timeDisplay(false, styles.length === 0)}
          </span>
          <div class="header-actions">
            <button class="clear-all">清空全部</button>
            <button class="copy-btn">复制代码</button>
            <button class="sync-btn" title="同步当前配置到定制脚本-样式表中，为避免样式重复，可在同步后选择点击'清空全部'">同步样式</button>
          </div>
        </div>
        <div class="preview-content">
          ${this.renderPre()}
        </div>
      </div>
    `
  }

  /**
   * 渲染时间显示
   * @param update 是否更新显示
   * @param empty 是否清空
   */
  private timeDisplay(update: boolean = false, empty: boolean = false) {
    const $timeDisplay = this.getElement('timeDisplay')

    if (empty) {
      $timeDisplay?.html('')
      return ''
    }

    const lastUpdateTime = update ? new Date() : this.lastUpdateTime
    const content = `
      创建于：${this.loadCreateTime()} | 
      更新于：${ToolKit.time.formatTime(lastUpdateTime)}
    `

    if (update) {
      $timeDisplay?.html(content)
      return ''
    }
    else {
      return content
    }
  }

  /**
   * 渲染预览内容
   */
  renderPre() {
    const content = this.generatePreviewContent(this.groupStyles)
    return `<pre>${content}</pre>`
  }

  /**
   * 渲染预览内容
   */
  renderEmpty() {
    return `<pre>${SYMBOLS.STATUS.EXCLAMATION} 未选择页面元素或元素样式，无法生成！</pre>`
  }

  /**
   * 组件样式
   */
  styles(): string {
    return styles(this.className)
  }

  /**
   * 加载/初始化创建时间
   */
  private loadCreateTime(): string {
    try {
      // 尝试从本地存储读取
      const saved = this.storage.get(this.createTimeStorageKey) as string
      if (saved) {
        return JSON.parse(saved)
      }
    }
    catch (e) {
      logger.warn(`[${this.name}] 读取创建时间失败:`, e)
    }

    // 默认使用当前时间
    const now = ToolKit.time.formatTime(new Date())
    this.saveCreateTime(now)
    return now
  }

  /**
   * 保存创建时间
   */
  private saveCreateTime(date: string): void {
    try {
      this.storage.set(this.createTimeStorageKey, JSON.stringify(date))
    }
    catch (e) {
      logger.warn(`[${this.name}] 保存创建时间失败:`, e)
    }
  }

  /**
   * 按照选择器分组样式
   * @param styles 样式配置
   */
  private groupStylesBySelector(styles: AdjustElementStyleConfig[]): AdjustElementStyleGroupConfig {
    return styles.reduce((acc, style) => {
      const { selector, property, value, updateTime, id } = style

      // 优先使用存储的时间，其次用当前时间
      const isLatestSelector = this.latestSelector && this.latestSelector === selector
      const selectorUpdateTime = isLatestSelector ? Date.now() : this.selectorTimeMap.get(selector) || Date.now()

      if (!acc[selector]) {
        acc[selector] = { meta: {
          id,
          name: style.name,
          category: style.category,
          updateTime: selectorUpdateTime,
        }, styles: [] }

        // 持久化选择器的更新时间
        if (isLatestSelector || !this.selectorTimeMap.get(selector)) {
          this.selectorTimeMap.set(selector, selectorUpdateTime)
        }
      }
      acc[selector].styles.push({ property, value, updateTime })
      return acc
    }, {} as AdjustElementStyleGroupConfig)
  }

  /**
   * 生成预览内容
   * @param groupedStyles 分组后的样式配置
   */
  private generatePreviewContent(groupedStyles: AdjustElementStyleGroupConfig): string {
    let lineCount = 0
    const lines: string[] = []

    // 按选择器组的更新时间降序排序
    const sortedGroups = Object.entries(groupedStyles).sort(([, a], [, b]) => {
      return (b.meta.updateTime || 0) - (a.meta.updateTime || 0)
    })

    const latestSelector = sortedGroups[0]?.[0] || ''

    sortedGroups.forEach(([selector, config]) => {
      // 生成选择器注释行
      lineCount++

      const id = config.meta.id

      const commitText = selector && selector.length > 0 ? `/* ${config.meta.category}-${config.meta.name} */` : `/* ${SYMBOLS.STATUS.WARNING} 无效样式：未选择页面元素 */`
      const commentLine = this.generateLine(
        lineCount,
        commitText,
        false,
        id,
        '',
        false,
        true,
      )

      lines.push(commentLine)

      // 生成选择器行
      lineCount++
      const selectorText = selector && selector.length > 0 ? `${selector} {` : '{'
      const selectorLine = this.generateLine(lineCount, selectorText)
      lines.push(selectorLine)

      // 生成样式属性行
      const sortedStyles = [...config.styles].sort((a, b) => a.property.localeCompare(b.property))
      const latestUpdateTime = Math.max(
        ...sortedStyles.map(style => style.updateTime || 0),
      )
      sortedStyles.forEach((style) => {
        lineCount++
        const isNew = style.updateTime === latestUpdateTime
        const value = this.formatStyleValue(style.property, style.value)
        const line = this.generateLine(
          lineCount,
          `  ${style.property}: ${value};`,
          true,
          id,
          style.property,
          isNew && selector === latestSelector,
        )
        lines.push(line)
      })

      // 生成闭合括号行
      lineCount++
      lines.push(this.generateLine(lineCount, `}`))

      // 添加空行分隔
      lineCount++
      lines.push('')
    })

    return lines.join('\n')
  }

  /**
   * 生成预览行
   * @param number 行号
   * @param content 内容
   * @param isStyleProperty 是否为样式属性
   * @param id 选择器 ID
   * @param property 样式属性
   * @param highlight 是否高亮
   * @param isComment 是否为注释
   */
  private generateLine(
    number: number,
    content: string,
    isStyleProperty: boolean = false,
    id?: string,
    property?: string,
    highlight = false,
    isComment = false,
  ): string {
    const classes = [
      highlight ? 'highlight' : '',
      isComment ? 'comment' : '',
      isStyleProperty ? 'style-property' : '',
    ].filter(Boolean).join(' ')

    const dataId = isStyleProperty && id && property
      ? this.styleIdManager.getStyleId(id, property)
      : ''

    return `
      <div class="line ${classes}"
        ${dataId ? `data-id="${dataId}"` : ''}
        ${isStyleProperty && property ? `data-property="${property}"` : ''}>
        <span class="delete-btn" data-line="${number}">${this.lineDeleteIcon}</span>
        <div class="content">${content}</div>
      </div>
    `
  }

  /**
   * 格式化样式值
   * @param property 样式属性
   * @param value 样式值
   */
  private formatStyleValue(property: string, value: string): string {
    if (property.includes('-color') && value.split(' ').length === 4) {
      const [top, right, bottom, left] = value.split(' ')
      return `${top} ${right} ${bottom} ${left}`
    }
    return value
  }

  /**
   * 组件更新
   * @param styles 样式配置
   * @param sourceType 数据来源
   * @param latestSelector 最新选择器
   */
  updateUI(styles: AdjustElementStyleConfig[], sourceType: DataChangeType = 'add', latestSelector?: string): void {
    super.updateUI()

    if (latestSelector)
      this.latestSelector = latestSelector

    // 如果配置为空，则更新创建时间
    if (styles.length === 0) {
      this.saveCreateTime(ToolKit.time.formatTime(new Date()))
    }

    // 更新元数据
    this.groupStyles = this.groupStylesBySelector(styles)
    this.timeDisplay(true, Object.keys(this.groupStyles).length === 0)

    const $container = this.getElement('codeContainer')
    if ($container) {
      if (sourceType !== 'clear' && styles.length === 0) {
        $container.html(this.renderEmpty())
      }
      else {
        $container.html(this.renderPre())
      }

      this.bindLineActions() // 只重新绑定行内操作
    }
    this.props.onUpdated(sourceType)
  }

  /**
   * 组件事件 - 先解绑再绑定，避免事件绑定和组件更新的循环调用，导致内存泄漏
   */
  bindEvents(): void {
    this.bindMainButtons()
    this.bindLineActions()
  }

  /**
   * 获取样式配置
   */
  private getStylesConfig(): string {
    const $pre = this.getElement('codeConfig')
    return $pre?.map((_, el) => {
      return $(el).text() || ''
    }).get().join('\n').trim() || ''
  }

  /**
   * 绑定主要按钮事件（复制和清空）
   */
  private bindMainButtons() {
    // 绑定复制按钮事件
    this.getElement('copyBtn')?.on('click', async (e) => {
      const $button = $(e.currentTarget)
      await ToolKit.dom.performCopy(this.getStylesConfig(), $button, CopyButtonUpdateType.TextContent)
    })

    // 绑定清空按钮
    this.getElement('clearAll')?.on('click', () => {
      this.getElement('codeContainer')?.html('')
      this.timeDisplay(false, true)
      this.props.onClickClearAll()
    })

    // 绑定同步按钮
    this.getElement('syncBtn')?.on('click', () => {
      this.handleSyncStyles()
    })
  }

  /**
   * 同步样式到样式表中
   */
  private handleSyncStyles() {
    const $css = $('#custom-css-text')
    const styles = this.getStylesConfig()

    if (!$css.length || styles.length === 0)
      return

    $css.val(styles)
  }

  /**
   * 绑定行内操作事件（删除行）
   */
  private bindLineActions() {
    // 绑定删除行事件
    this.getElement('lineDeleteBtn')?.off('click').on('click', (e) => {
      const lineNum = $(e.currentTarget).data('line')
      this.handleDeleteLine(lineNum)
    })
  }

  /**
   * 处理删除行操作
   * @param lineNum 要删除的行号
   */
  private handleDeleteLine(lineNum: number) {
    try {
    // 1. 验证行号有效性
      if (!lineNum || lineNum <= 0) {
        throw new Error(`无效的行号: ${lineNum}`)
      }

      // 2. 查找目标行
      const $line = this.getElement('codeContainer')?.find(`[data-line="${lineNum}"]`).closest('.line')
      if (!$line || $line.length === 0) {
        throw new Error(`未找到行号 ${lineNum} 对应的内容`)
      }

      // 3. 解析行内容
      const id = $line.data('id')
      const property = $line.data('property')
      const lineContent = $line.text().trim()

      // 构造回调参数
      const payload: DeleteLinePayload = {
        lineNum,
        property,
        id,
      }

      // 4.触发回调
      this.props.onClickDeleteLine(lineNum, payload)

      logger.info(`成功删除行 ${lineNum}: ${lineContent}`)
    }
    catch (error: any) {
      logger.error(`删除行 ${lineNum} 失败:`, error)
    }
  }
}
